﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Web;
using System.Xml;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;

using Microsoft.SqlServer.Server;

namespace PickGold.SqlServer
{
	/// <summary>
	/// Common
	/// </summary>
	public static class Net
	{
		private static int InnerSendUdpData(byte[] data, string host, int port)
		{
			if (data == null || data.Length == 0)
				return 0;

			if (string.IsNullOrEmpty(host))
				host = "127.0.0.1";
			if (port == 0)
				port = 52866;

			//var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			//var ips = Dns.GetHostAddresses(host);
			//for (var i = 0; i < ips.Length; i++)
			//{
			//    if (ips[i].AddressFamily == socket.AddressFamily)
			//    {
			//        if (ips[i] == IPAddress.Broadcast)
			//            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
			//        i = socket.SendTo(data, 0, data.Length, SocketFlags.None, new IPEndPoint(ips[i], port));
			//        socket.Close();
			//        return i;
			//    }
			//}
			//return -1;

			if (Common.InnerItems == null)
				Common.InnerItems = new Hashtable();
			var udp = Common.InnerItems[typeof(UdpClient)] as UdpClient;
			if (udp == null)
			{
				udp = new UdpClient();
				Common.InnerItems[typeof(UdpClient)] = udp;
			}
			var i = data.Length;
			try
			{
				if (Math.Abs(port) > ushort.MaxValue || host.IndexOfAny(Common.SQL_COMMAND_FLAG) >= 0)
				{
					using (var con = Common.OpenConnection(null))
					{
						using (var cmd = con.CreateCommand())
						{
							cmd.CommandText = host;
							if (host.IndexOfAny(Common.SQL_COMMAND_FLAG) < 0)
								cmd.CommandType = CommandType.StoredProcedure;
							cmd.Parameters.Add(new SqlParameter("@data", SqlDbType.VarBinary, -1)).Value = data;
							cmd.Parameters.Add(new SqlParameter("@host", SqlDbType.NVarChar, -1)).Value = host;
							cmd.Parameters.Add(new SqlParameter("@port", SqlDbType.Int)).Value = port;
							i = cmd.ExecuteNonQuery();
						}
						Common.CloseConnection(con, null);
					}
				}
				else
				{
					//SqlContext.Pipe.Send("Will send data length:" + i);
					if (port > 0)
						i = udp.Send(data, i, host, port);
					else
						udp.BeginSend(data, i, host, port * -1, new AsyncCallback(result => { try { (result.AsyncState as UdpClient).EndSend(result); } catch { } }), udp);
					//SqlContext.Pipe.Send("Fact sent data length:" + i);
					//SqlContext.Pipe.Send("Data from [" + udp.Client.LocalEndPoint + "] to [" + host + ":" + port + "].");
				}
			}
			catch (Exception x)
			{
				if (SqlContext.IsAvailable && SqlContext.Pipe != null)
					SqlContext.Pipe.Send(x.ToString());
				i *= -1;
			}
			return i;
		}

		/// <summary/>
		[SqlProcedure]
		[Description("发送UDP数据包")]
		[return: Description("如果成功，返回发送字节数，失败返回负数")]
		public static SqlInt32 SendUdpText([Description("要发送的数据")] SqlString data, [Description("远程主机名或IP地址，如果包含括号或等号则表示为数据命令")] SqlString host, [Description("远程端口，正表示同步发送，负表示异步发送，异步发送无法得知发送是否成功，为零或默认 52866")] SqlInt32 port)
		{
			if (data.IsNull || string.IsNullOrEmpty(data.Value))
				return 0;

			var bs = Encoding.UTF8.GetBytes(data.Value);
			return Net.InnerSendUdpData(bs, host.IsNull ? null : host.Value, port.IsNull ? 0 : port.Value);
		}

		/// <summary/>
		[SqlProcedure]
		[Description("发送UDP数据包")]
		[return: Description("如果成功，返回发送字节数，失败返回负数")]
		public static SqlInt32 SendUdpData([Description("要发送的数据")] SqlBinary data, [Description("远程主机名或IP地址，如果包含括号或等号则表示为数据命令")] SqlString host, [Description("远程端口，正表示同步发送，负表示异步发送，异步发送无法得知发送是否成功，为零或默认 52866")] SqlInt32 port)
		{
			if (data.IsNull || data.Length == 0)
				return 0;

			return Net.InnerSendUdpData(data.Value, host.IsNull ? null : host.Value, port.IsNull ? 0 : port.Value);
		}

		/// <summary/>
		[SqlProcedure]
		[Description("发送UDP数据包")]
		[return: Description("如果成功，返回发送字节数，失败返回负数")]
		public static SqlInt32 SendUdpXml([Description("要发送的数据")] SqlXml data, [Description("远程主机名或IP地址，如果包含括号或等号则表示为数据命令")] SqlString host, [Description("远程端口，正表示同步发送，负表示异步发送，异步发送无法得知发送是否成功，为零或默认 52866")] SqlInt32 port)
		{
			if (data.IsNull)
				return 0;

			return Net.InnerSendUdpData(Encoding.UTF8.GetBytes(data.Value), host.IsNull ? null : host.Value, port.IsNull ? 0 : port.Value);
		}

		/// <summary>
		/// DownloadString
		/// </summary>
		/// <param name="url">url</param>
		/// <returns>url result</returns>
		[SqlFunction(IsDeterministic = false, IsPrecise = false)]
		public static SqlString DownloadString(SqlString url)
		{
			if (url.IsNull || string.IsNullOrEmpty(url.Value))
				return null;

			var wc = new WebClient();
			var result = wc.DownloadString(url.Value);
			wc.Dispose();
			return new SqlString(result);
		}

		/// <summary>
		/// DownloadData
		/// </summary>
		/// <param name="url">url</param>
		/// <returns>url result</returns>
		[SqlFunction(IsDeterministic = false, IsPrecise = false)]
		public static SqlBinary DownloadData(SqlString url)
		{
			if (url.IsNull || string.IsNullOrEmpty(url.Value))
				return null;

			var wc = new WebClient();
			var result = wc.DownloadData(url.Value);
			wc.Dispose();
			return new SqlBinary(result);
		}
		
		/// <summary>
		/// UploadString
		/// </summary>
		/// <param name="url">url</param>
		/// <param name="data">data</param>
		/// <returns>url result</returns>
		[SqlFunction(IsDeterministic = false, IsPrecise = false)]
		public static SqlString UploadString(SqlString url, SqlString data)
		{
			if (url.IsNull || string.IsNullOrEmpty(url.Value))
				return null;

			if (data.IsNull || string.IsNullOrEmpty(data.Value))
				return null;

			var wc = new WebClient();
			var result = wc.UploadString(url.Value, data.Value);
			wc.Dispose();
			return new SqlString(result);
		}

		/// <summary>
		/// UploadData
		/// </summary>
		/// <param name="url">url</param>
		/// <param name="data">data</param>
		/// <returns>url result</returns>
		[SqlFunction(IsDeterministic = false, IsPrecise = false)]
		public static SqlBinary UploadData(SqlString url, SqlBinary data)
		{
			if (url.IsNull || string.IsNullOrEmpty(url.Value))
				return null;

			if (data.IsNull || data.Value == null || data.Value.Length == 0)
				return null;

			var wc = new WebClient();
			var result = wc.UploadData(url.Value, data.Value);
			wc.Dispose();
			return new SqlBinary(result);
		}
		/*
		/// <summary>
		/// HtmlDecode
		/// </summary>
		/// <param name="input">input</param>
		/// <returns>input returns</returns>
		[SqlFunction]
		public static SqlString HtmlDecode(SqlString input)
		{
			if (input.IsNull || string.IsNullOrEmpty(input.Value))
				return null;

			return new SqlString(HttpUtility.HtmlDecode(input.Value));
		}

		/// <summary>
		/// HtmlEncode
		/// </summary>
		/// <param name="input">input</param>
		/// <returns>input returns</returns>
		[SqlFunction]
		public static SqlString HtmlEncode(SqlString input)
		{
			if (input.IsNull || string.IsNullOrEmpty(input.Value))
				return null;

			return new SqlString(HttpUtility.HtmlEncode(input.Value));
		}

		/// <summary>
		/// UrlDecode
		/// </summary>
		/// <param name="input">input</param>
		/// <returns>input returns</returns>
		[SqlFunction]
		public static SqlString UrlDecode(SqlString input)
		{
			if (input.IsNull || string.IsNullOrEmpty(input.Value))
				return null;

			return new SqlString(HttpUtility.UrlDecode(input.Value));
		}

		/// <summary>
		/// UrlEncode
		/// </summary>
		/// <param name="input">input</param>
		/// <returns>input returns</returns>
		[SqlFunction]
		public static SqlString UrlEncode(SqlString input)
		{
			if (input.IsNull || string.IsNullOrEmpty(input.Value))
				return null;

			return new SqlString(HttpUtility.UrlEncode(input.Value));
		}

		/// <summary>
		/// ParseQueryString
		/// </summary>
		/// <param name="input">input</param>
		/// <returns>input returns</returns>
		[SqlFunction(FillRowMethodName = "FillRowQueryItem", TableDefinition = "[key] [nvarchar](MAX), [value] [nvarchar](MAX)")]
		public static IEnumerable ParseQueryString(SqlString input)
		{
			if (input.IsNull || string.IsNullOrEmpty(input.Value))
				return null;

			var ls = new List<SqlString[]>();
			var noc = HttpUtility.ParseQueryString(input.Value);
			foreach (var key in noc.AllKeys)
			{
				ls.Add(new SqlString[] { new SqlString(key), new SqlString(noc[key]) });
				var vs = noc.GetValues(key);
				if (vs == null || vs.Length <= 1)
					continue;

				foreach (var v in vs)
					ls.Add(new SqlString[] { new SqlString(key), new SqlString(v) });
			}
			return ls.ToArray();
		}

		private static void FillRowQueryItem(object item, out SqlString name, out SqlString drop)
		{
			var os = item as SqlString[];
			name = os[0];
			drop = os[1];
		}
		*/
	}
	/*
	 * 消息 6503，级别 16，状态 12，第 1 行
	 * 在 SQL 目录中找不到程序集 'system.web, version=2.0.0.0, culture=neutral, publickeytoken=b03f5f7f11d50a3a.'。
	 * */
}
